package OOPAndMultiThreadArchiveManager.Client.Views;

import OOPAndMultiThreadArchiveManager.Client.Controllers.HttpController;
import OOPAndMultiThreadArchiveManager.Client.Models.ApiReceive;
import OOPAndMultiThreadArchiveManager.Client.Models.ApiResponse;
import OOPAndMultiThreadArchiveManager.Client.Models.Archive;
import OOPAndMultiThreadArchiveManager.Client.Models.UserInfo;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ArchiveManageView {
    private final Vector<String> columns = new Vector<>(List.of(new String[]{"档案号", "上传者", "上传时间", "文件名", "描述"}));
    private JPanel panel;
    private JTabbedPane tabbedPane1;
    private JPanel downloadPanel;
    private JPanel uploadPanel;
    private JTable table;
    private JButton downloadButton;
    private JTextField textField1;
    private JButton browseButton;
    private JTextArea textArea1;
    private JButton uploadButton;
    private JProgressBar uploadProgressBar;
    private JProgressBar downloadProgressBar;
    private UserInfo userInfo;
    private File openFile;
    private JFrame frame;
    private ObjectMapper objectMapper = new ObjectMapper();
    private Vector<Archive> allArchives = new Vector<>();
    private Vector<Vector<String>> allArchivesArray = new Vector<>();
    private Vector<Integer> progress = new Vector<>(List.of(0));
    private ExecutorService threadPool = Executors.newFixedThreadPool(2);

    public ArchiveManageView(UserInfo userInfo, int index) {
        uploadProgressBar.setValue(progress.get(0));
        this.userInfo = userInfo;
        getAllArchivesAsync();

        table.getTableHeader().setReorderingAllowed(false);

        tabbedPane1.setSelectedIndex(index);
        accessCheck();

        browseButton.addActionListener(e -> browse());

        uploadButton.addActionListener(e -> {
            threadPool.shutdownNow();
            threadPool = Executors.newFixedThreadPool(2);
            boolean[] started = { false };
            var freshAsync = CompletableFuture.runAsync(()->{
                uploadButton.setEnabled(false);
                frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
                started[0] = true;
                while(true){
                    try { Thread.sleep(200); } catch (InterruptedException ignored) {}
                    uploadProgressBar.setValue(progress.get(0));
                }
            }, threadPool);
            CompletableFuture.runAsync(()->{
                    while(!started[0])
                        try { Thread.sleep(200); } catch (InterruptedException ignored) {}
                    upload();
                }, threadPool)
                .whenCompleteAsync((res, exception)->{
                    uploadButton.setEnabled(true);
                    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                    freshAsync.cancel(true);
                    progress.set(0, 0);
                    uploadProgressBar.setValue(progress.get(0));
                });
        });

        downloadButton.addActionListener(e -> {
            threadPool.shutdownNow();
            threadPool = Executors.newFixedThreadPool(2);
            boolean[] started = { false };
            var freshAsync = CompletableFuture.runAsync(()->{
                downloadButton.setEnabled(false);
                frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
                started[0] = true;
                while(true){
                    try { Thread.sleep(200); } catch (InterruptedException ignored) {}
                    downloadProgressBar.setValue(progress.get(0));
                }
            }, threadPool);
            CompletableFuture.runAsync(()->{
                    while(!started[0])
                        try { Thread.sleep(200); } catch (InterruptedException ignored) {}
                    download();
                }, threadPool)
                .whenCompleteAsync((res, exception)->{
                    downloadButton.setEnabled(true);
                    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                    freshAsync.cancel(true);
                    progress.set(0, 0);
                    downloadProgressBar.setValue(progress.get(0));
                });
        });

        tabbedPane1.addChangeListener(e -> getAllArchivesAsync());

        frame = new JFrame("ArchiveManageView");
        frame.setContentPane(panel);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.pack();
        frame.setSize(600, 360);
        frame.setLocationRelativeTo(panel);
        frame.setVisible(true);
    }

    private static String byteToHexLower(byte[] b) {
        var hashcode = new StringBuilder();
        var stringTemp = "";
        for (var value : b) {
            stringTemp = Integer.toHexString(value & 0XFF);
            if (stringTemp.length() == 1) {
                hashcode.append("0").append(stringTemp);
            } else {
                hashcode.append(stringTemp);
            }
        }
        return hashcode.toString();
    }

    private void browse() {
        var chooser = new JFileChooser();
        int option = chooser.showOpenDialog(chooser);
        if (option != JFileChooser.APPROVE_OPTION) {
            return;
        }
        openFile = chooser.getSelectedFile();
        textField1.setText(openFile.getPath());
    }

    private void accessCheck() {
        uploadPanel.setVisible(userInfo.role.equals("Administrator") || userInfo.role.equals("Operator"));
    }

    private void getAllArchivesAsync() {
        var request = new ApiReceive();
        request.user.name = userInfo.user;
        request.user.password = userInfo.password;
        request.api = ApiReceive.Api.GET_ArchiveList;
        CompletableFuture.supplyAsync(() -> new HttpController().Request(request))
                .whenCompleteAsync(((response, throwable) -> {
                    if (response.apiCode == ApiResponse.ApiCode.Success) {
                        allArchives = objectMapper.convertValue(response.data, new TypeReference<Vector<Archive>>(){});
                        allArchivesArray.clear();
                        var simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        for (var archive : allArchives) {
                            allArchivesArray.add(new Vector<>(List.of(new String[]{
                                    String.valueOf(archive.id),
                                    archive.uploader,
                                    simpleDateFormat.format(archive.dateTime),
                                    archive.fileName,
                                    archive.description})));
                        }
                    } else if (response.apiCode == ApiResponse.ApiCode.Unauthorized) {
                        JOptionPane.showMessageDialog(frame, "验证失败，请检查你的账号和密码。");
                    }
                    table.setModel(new DefaultTableModel(allArchivesArray, columns) {
                        @Override
                        public boolean isCellEditable(int row, int column) {
                            return false;
                        }
                    });
                }));
    }

    private void upload() {
        if (openFile == null) {
            JOptionPane.showMessageDialog(frame, "未选择任何文件");
        }
        try {
            progress.set(0, 0);
            var fileInputStream = new FileInputStream(openFile);
            var buffer = new byte[1024];
            var length = 0;
            var messageDigest = MessageDigest.getInstance("SHA-256");
            while ((length = fileInputStream.read(buffer)) != -1) {
                messageDigest.update(buffer, 0, length);
            }
            fileInputStream.close();
            var digest = messageDigest.digest();
            var sha256 = byteToHexLower(digest);

            progress.set(0, 10);

            var archive = new Archive(0, sha256, openFile.getName(), textArea1.getText(), new Date(), userInfo.user);
            var request = new ApiReceive();
            request.api = ApiReceive.Api.POST_Archive;
            request.user.name = userInfo.user;
            request.user.password = userInfo.password;
            request.data = archive;
            var responseBefore = new HttpController().Request(request);
            if (responseBefore.apiCode == ApiResponse.ApiCode.Unauthorized) {
                JOptionPane.showMessageDialog(frame, "验证失败，请检查你的账号和密码。");
                return;
            } else if (responseBefore.apiCode == ApiResponse.ApiCode.Failed) {
                JOptionPane.showMessageDialog(frame, objectMapper.convertValue(responseBefore.data, String.class));
                return;
            }
            progress.set(0,50);

            var responseAfter = new HttpController().Upload(openFile, progress);
            Thread.sleep(1000);
            if (responseAfter.apiCode == ApiResponse.ApiCode.Unauthorized) {
                JOptionPane.showMessageDialog(frame, "验证失败，请检查你的账号和密码。");
                return;
            } else if (responseAfter.apiCode == ApiResponse.ApiCode.Failed) {
                JOptionPane.showMessageDialog(frame, objectMapper.convertValue(responseBefore.data, String.class));
                return;
            }
            progress.set(0,100);

            JOptionPane.showMessageDialog(frame, "文件上传成功");
        } catch (NoSuchAlgorithmException | IOException e) {
            JOptionPane.showMessageDialog(frame, "上传文件失败");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

    }

    private void download() {
        for (int i = 0; i < 5; i++) {
            if (table.isColumnSelected(i)) {
                break;
            } else if (i == 4) {
                return;
            }
        }

        var request = new ApiReceive();
        request.user.name = userInfo.user;
        request.user.password = userInfo.password;
        request.api = ApiReceive.Api.GET_Archive;

        var id = Integer.parseInt((String) table.getValueAt(table.getSelectedRow(), 0));
        var downloadArchive = new Archive();
        for (var archive : allArchives) {
            if (archive.id == id) {
                downloadArchive = archive;
                break;
            }
        }
        request.data = downloadArchive;

        var fileChooser = new JFileChooser();
        fileChooser.setSelectedFile(new File(downloadArchive.fileName));
        var option = fileChooser.showSaveDialog(frame);
        if (option != JFileChooser.APPROVE_OPTION) {
            return;
        }
        var file = fileChooser.getSelectedFile();
        var result = new HttpController().DownLoad(request, file, progress);
        if (result) {
            JOptionPane.showMessageDialog(frame, "下载成功");
        } else {
            JOptionPane.showMessageDialog(frame, "下载失败");
        }
    }
}
